//
//  ProtocolHandler.cpp
//  Ragnarok Battle Online
//
//  Created by xudexin on 13-4-17.
//
//
#include "CCPomelo.h"
#include "Pomelo.h"
#include <errno.h>
#pragma comment(lib, "IPHLPAPI.lib")  
#pragma comment(lib, "Psapi.lib") 

class PomeloContent_ {
public:
    PomeloContent_(){
        pTarget = NULL;
        pSelector = NULL;
    }
    ~PomeloContent_(){
        
    }
    Ref* pTarget;
    SEL_CallFuncND pSelector;
};

class PomeloReponse_ {
public:
    PomeloReponse_(){
        request = NULL;
        docs = NULL;
    }
    ~PomeloReponse_(){
        
    }
    int status;
    pc_request_t *request;
    json_t *docs;
};

class PomeloEvent_ {
public:
    PomeloEvent_(){
        docs = NULL;
    }
    ~PomeloEvent_(){
        
    }
    int status;
    std::string event;
    json_t *docs;
};

class PomeloNotify_ {
public:
    PomeloNotify_(){
        notify = NULL;
    }
    ~PomeloNotify_(){
        
    }
    int status;
    pc_notify_t *notify;
};

class PomeloConnect_ {
public:
    PomeloConnect_(){
        req = NULL;
    }
    ~PomeloConnect_(){
        
    }
    int status;
    pc_connect_t *req;
    PomeloContent_ *content;
};

static Pomelo *s_Pomelo = NULL; // pointer to singleton

void   cc_pomelo_on_ansync_connect_cb  (pc_connect_t* conn_req, int status){
    //log("data = %p\n", conn_req->data);
    if (conn_req) {
        pc_connect_req_destroy(conn_req);
    }
    s_Pomelo->connectCallBack(status);

}

void cc_pomelo_on_notify_cb(pc_notify_t *ntf, int status){
    
    s_Pomelo->lockNotifyQeueue();
    
    PomeloNotify_ *notify = new PomeloNotify_;
    notify->notify = ntf;
    notify->status = status;
    
    s_Pomelo->pushNotiyf(notify);
    
    s_Pomelo->unlockNotifyQeueue();
}


void cc_pomelo_on_event_cb(pc_client_t *client, const char *event, void *data) {
    
    s_Pomelo->lockEventQeueue();
    
    PomeloEvent_ *ev = new PomeloEvent_;
    ev->event = event;
    ev->docs = (json_t *)data;
    json_incref(ev->docs);
    
    s_Pomelo->pushEvent(ev);
    
    s_Pomelo->unlockEventQeueue();
    
}

void cc_pomelo_on_request_cb(pc_request_t *request, int status, json_t *docs) {
    
    s_Pomelo->lockReponsQeueue();
    
    PomeloReponse_ *response = new PomeloReponse_;
    response->request = request;
    response->status = status;
    response->docs = docs;
    json_incref(docs);
    
    s_Pomelo->pushReponse(response);
    
    s_Pomelo->unlockReponsQeueue();
    
}

Pomelo::Pomelo(){
    Director::getInstance()->getScheduler()->schedule(schedule_selector(Pomelo::dispatchCallbacks), this, 0, false);
    Director::getInstance()->getScheduler()->pauseTarget(this);
    client = pc_client_new();
	/*
    pthread_mutex_init(&reponse_queue_mutex, NULL);
    pthread_mutex_init(&event_queue_mutex, NULL);
    pthread_mutex_init(&notify_queue_mutex, NULL);
    pthread_mutex_init(&task_count_mutex, NULL);
    pthread_mutex_init(&connect_mutex, NULL);
	*/
    task_count = 0;
    connect_status = 0;
    connect_content = NULL;
}

Pomelo::~Pomelo(){
    Director::getInstance()->getScheduler()->unschedule(schedule_selector(Pomelo::dispatchCallbacks), s_Pomelo);
}

void Pomelo::dispatchRequest(){
    lockReponsQeueue();
    PomeloReponse_ *response = popReponse();
    if (response) {
        PomeloContent_ * content = NULL;
        if (request_content.find(response->request)!=request_content.end()) {
            content = request_content[response->request];
            request_content.erase(response->request);
        }
        if (content) {
            
            Ref *pTarget = content->pTarget;
            SEL_CallFuncND pSelector = content->pSelector;
            if (pTarget && pSelector)
            {
                PomeloReponse resp;
                resp.status = response->status;
                resp.docs = response->docs;
                (pTarget->*pSelector)((Node *)this,&resp);
            }
        }else{
            log("dispatch response:\r\nlost content");
        }
        json_decref(response->docs);
        json_decref(response->request->msg);
        pc_request_destroy(response->request);
        delete response;
    }
    unlockReponsQeueue();
}

void Pomelo::dispatchEvent(){
    lockEventQeueue();
    PomeloEvent_ *event = popEvent();
    if (event) {
        PomeloContent_ * content = NULL;
        if (event_content.find(event->event)!=event_content.end()) {
            content = event_content[event->event];
        }
        if (content) {
            Ref *pTarget = content->pTarget;
            SEL_CallFuncND pSelector = content->pSelector;
            if (pTarget && pSelector)
            {
                PomeloReponse resp;
                resp.status = event->status;
                resp.docs = event->docs;
                (pTarget->*pSelector)((Node *)this,&resp);
            }
        }else{
            log("dispatch event::\r\n lost %s content",event->event.c_str());
        }
        json_decref(event->docs);
        delete event;
    }
    unlockEventQeueue();
}

void Pomelo::dispatchNotify(){
    lockNotifyQeueue();
    PomeloNotify_ *ntf = popNotify();
    if (ntf) {
        PomeloContent_ * content = NULL;
        if (notify_content.find(ntf->notify)!=notify_content.end()) {
            content = notify_content[ntf->notify];
            notify_content.erase(ntf->notify);
        }
        if (content) {
            Ref *pTarget = content->pTarget;
            SEL_CallFuncND pSelector = content->pSelector;
            if (pTarget && pSelector)
            {
                PomeloReponse resp;
                resp.status = ntf->status;
                resp.docs = NULL;
                (pTarget->*pSelector)((Node *)this,&resp);
            }
        }else{
            log("dispatch notify:\r\nlost content");
        }
        json_decref(ntf->notify->msg);
        pc_notify_destroy(ntf->notify);
        delete ntf;
    }
    unlockNotifyQeueue();
}

void Pomelo::connectCallBack(){
    if (connect_content) {
        Ref *pTarget = connect_content->content->pTarget;
        SEL_CallFuncND pSelector = connect_content->content->pSelector;
        if (pTarget && pSelector)
        {
            PomeloReponse resp;
            resp.status = connect_content->status;
            resp.docs = NULL;
            (pTarget->*pSelector)((Node *)this,&resp);
        }
        connect_status  = 0;
        desTaskCount();
        delete connect_content;
        connect_content = NULL;
    }
   
}

void Pomelo::dispatchCallbacks(float delta){
    dispatchNotify();
    dispatchEvent();
    dispatchRequest();
    if (connect_status==1) {
        connectCallBack();
    }
    
  //  pthread_mutex_lock(&task_count_mutex);
    
    if (task_count==0) {
        Director::getInstance()->getScheduler()->pauseTarget(this);
    }
  //  pthread_mutex_unlock(&task_count_mutex);
    
}

void Pomelo::destroyInstance()
{
    if (s_Pomelo) {
        s_Pomelo->release();
    }
}

Pomelo* Pomelo::getInstance()
{
    if (s_Pomelo == NULL) {
        s_Pomelo = new Pomelo();
    }
    return s_Pomelo;
}

int Pomelo::connect(const char* addr,int port){
    struct sockaddr_in address;
    memset(&address, 0, sizeof(struct sockaddr_in));
    address.sin_family = AF_INET;
    address.sin_port = htons(port);
    address.sin_addr.s_addr = inet_addr(addr);
    
    client = pc_client_new();
   
    int ret = pc_client_connect(client, &address);
    if(ret) {
        log("pc_client_connect error:%d",errno);
        pc_client_destroy(client);
    }
    return  ret;
}

void Pomelo::asyncConnect(const char* addr,int port,Ref* pTarget, SEL_CallFuncND pSelector){
    if (!connect_content) {
        connect_status = 0;
        connect_content = new PomeloConnect_;
        connect_content->content = new PomeloContent_;
        connect_content->content->pTarget = pTarget;
        connect_content->content->pSelector = pSelector;
    }else{
        log("can not call again before the first connect callback");
        return ;
    }
    
    struct sockaddr_in address;
    memset(&address, 0, sizeof(struct sockaddr_in));
    address.sin_family = AF_INET;
    address.sin_port = htons(port);
    address.sin_addr.s_addr = inet_addr(addr);
    
    client = pc_client_new();
    pc_connect_t* async = pc_connect_req_new(&address);
    int ret = pc_client_connect2(client,async,cc_pomelo_on_ansync_connect_cb);
    if(ret) {
        log("pc_client_connect2 error:%d",errno);
        pc_client_destroy(client);
        cc_pomelo_on_ansync_connect_cb(NULL,ret);
    }

}

void Pomelo::stop(){
    if(client){
//      pc_client_stop(client);
        pc_client_destroy(client);
    }
}

void Pomelo::cleanup(){
    cleanupEventContent();
    cleanupNotifyContent();
    cleanupRequestContent();
   // pthread_mutex_lock(&task_count_mutex);
   // pthread_mutex_unlock(&task_count_mutex);
}

void Pomelo::cleanupEventContent(){
    std::map<std::string,PomeloContent_*>::iterator iter;
    for (iter = event_content.begin();iter != event_content.end();iter++) {
        PomeloContent_ *content = iter->second;
        delete  content;
        pc_remove_listener(client, iter->first.c_str(), cc_pomelo_on_event_cb);
    }
    event_content.clear();
}

void Pomelo::cleanupNotifyContent(){
    std::map<pc_notify_t*,PomeloContent_*>::iterator iter;
    for (iter = notify_content.begin();iter != notify_content.end();iter++) {
        PomeloContent_ *content = iter->second;
        delete  content;
    }
    notify_content.clear();
}

void Pomelo::cleanupRequestContent(){
    std::map<pc_request_t *,PomeloContent_*>::iterator iter;
    for (iter = request_content.begin();iter != request_content.end();iter++) {
        PomeloContent_ *content = iter->second;
        delete  content;
    }
    request_content.clear();
}

int Pomelo::request(const char*route,json_t *msg,Ref* pTarget, SEL_CallFuncND pSelector){
    
    pc_request_t *req   = pc_request_new();
    PomeloContent_ *content = new PomeloContent_;
    content->pTarget = pTarget;
    content->pSelector = pSelector;
    request_content[req] = content;
    return pc_request(client,req, route, json_deep_copy(msg), cc_pomelo_on_request_cb);
}

int Pomelo::notify(const char*route,json_t *msg,Ref* pTarget, SEL_CallFuncND pSelector){
    pc_notify_t *notify = pc_notify_new();
    PomeloContent_ *content = new PomeloContent_;
    content->pTarget = pTarget;
    content->pSelector = pSelector;
    notify_content[notify] = content;
    return pc_notify(client,notify, route, json_deep_copy(msg), cc_pomelo_on_notify_cb);
}

int Pomelo::addListener(const char* event,Ref* pTarget, SEL_CallFuncND pSelector){
    PomeloContent_ *content = new PomeloContent_;
    content->pTarget = pTarget;
    content->pSelector = pSelector;
    if (event_content.find(event)!=event_content.end()) {
        delete  event_content[event];
    }
    event_content[event] = content;
    return pc_add_listener(client, event, cc_pomelo_on_event_cb);
}

void Pomelo::removeListener(const char *event){
    if (event_content.find(event)!=event_content.end()) {
        delete  event_content[event];
    }
    event_content.erase(event);
    pc_remove_listener(client, event, cc_pomelo_on_event_cb);
}

void Pomelo::incTaskCount(){
   // pthread_mutex_lock(&task_count_mutex);
    task_count++;
   // pthread_mutex_unlock(&task_count_mutex);
    Director::getInstance()->getScheduler()->resumeTarget(s_Pomelo);
}

void Pomelo::desTaskCount(){
  //  pthread_mutex_lock(&task_count_mutex);
    task_count--;
  //  pthread_mutex_unlock(&task_count_mutex);
}

void Pomelo::lockReponsQeueue(){
 //   pthread_mutex_lock(&reponse_queue_mutex);
}

void Pomelo::unlockReponsQeueue(){
  //  pthread_mutex_unlock(&reponse_queue_mutex);
}

void Pomelo::lockEventQeueue(){
  //  pthread_mutex_lock(&event_queue_mutex);
}

void Pomelo::unlockEventQeueue(){
  //  pthread_mutex_unlock(&event_queue_mutex);
}

void Pomelo::lockNotifyQeueue(){
  //  pthread_mutex_lock(&notify_queue_mutex);
}

void Pomelo::unlockNotifyQeueue(){
  //  pthread_mutex_unlock(&notify_queue_mutex);
}

void Pomelo::lockConnectContent(){
  //  pthread_mutex_unlock(&connect_mutex);
}

void Pomelo::unlockConnectContent(){
 //   pthread_mutex_unlock(&connect_mutex);
}

void Pomelo::pushReponse(PomeloReponse_*response){
    reponse_queue.push(response);
    incTaskCount();
}

void Pomelo::pushEvent(PomeloEvent_* event){
    event_queue.push(event);
    incTaskCount();
}

void Pomelo::pushNotiyf(PomeloNotify_*notify){
    notify_queue.push(notify);
    incTaskCount();
}

void Pomelo::connectCallBack(int status){
    connect_status = 1;
    connect_content->status = status;
    incTaskCount();
}

PomeloReponse_*Pomelo::popReponse(){
    if (reponse_queue.size()>0) {
        PomeloReponse_ *response = reponse_queue.front();
        reponse_queue.pop();
        desTaskCount();
        return  response;
    }else{
        return  NULL;
    }
}

PomeloEvent_*Pomelo::popEvent(){
    if (event_queue.size()>0) {
        PomeloEvent_ *event = event_queue.front();
        event_queue.pop();
        desTaskCount();
        return  event;
    }else{
        return  NULL;
    }
}

PomeloNotify_*Pomelo::popNotify(){
    if (notify_queue.size()>0) {
        PomeloNotify_ *ntf = notify_queue.front();
        notify_queue.pop();
        desTaskCount();
        return  ntf;
    }else{
        return  NULL;
    }
}
